//-------------------------------------------------------------------------------------------------
// Copyright (c) Bradford W. Mott and Flare Contributors
// North Carolina State University, Department of Computer Science
// The IntelliMedia Group
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
// SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
// OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//-------------------------------------------------------------------------------------------------

using System.Collections;
using UnityEngine;

using Flare.Display;

/// <summary>
/// The SwfCameraOverlayPlayer class is a component that is added to a Unity camera
/// to render a SWF file as an overlay.
/// </summary>
public class SwfCameraOverlayPlayer : MonoBehaviour
{
    /// <summary>
    /// SwfAsset to load upon initialization
    /// </summary>
    public SwfAsset swfAsset = null;

    /// <summary>
    /// Indicates if the SWF should be rendered with a transparent background or not
    /// </summary>
    public bool transparent = false;

    /// <summary>
    /// Indicates if the SWF should be rendered after all cameras and Unity GUI
    /// </summary>
    public bool renderAtEndOfFrame = true;

    /// <summary>
    /// Indicates where to render the overlay in normalized coordinates relative to the
    /// camera's viewport
    /// </summary>
    public Rect rect = new Rect(0.0f, 0.0f, 1.0f, 1.0f);

    /// <summary>
    /// Indicates the bounds for the window (i.e., where the overlay is rendered
    /// in pixel coordinates)
    /// </summary>
    public Flare.Geom.Rectangle bounds
    {
        get
        {
            var cam = this.GetComponent<Camera>();
            return new Flare.Geom.Rectangle(
                cam.pixelRect.x + cam.pixelRect.width * this.rect.x,
                cam.pixelRect.y + cam.pixelRect.height * this.rect.y,
                cam.pixelRect.width * this.rect.width,
                cam.pixelRect.height * this.rect.height);
        }
    }

    /// <summary>
    /// Native window associated with the overlay
    /// </summary>
    public NativeWindow nativeWindow
    {
        get
        {
            return this.m_nativeWindow;
        }
        set
        {
            this.m_nativeWindow = value;
            this.UpdateNativeWindowProperties(true);
        }
    }
    private NativeWindow m_nativeWindow;

    public SwfCameraOverlayPlayer()
    {
    }

    public void Awake()
    {
        if (swfAsset != null)
        {
            this.nativeWindow = new NativeWindow(this.bounds, this.transparent, swfAsset.Bytes);
        }
    }

    public void Update()
    {
        if (this.nativeWindow != null)
        {
            this.UpdateNativeWindowProperties();
            this.nativeWindow.Update(Time.time);
        }
    }

    public IEnumerator OnPostRender()
    {
        if (this.nativeWindow != null)
        {
            if (renderAtEndOfFrame)
            {
                yield return new WaitForEndOfFrame();
            }

            // Save camera posiiton and rotation
            Transform cameraTransform = Camera.current.transform;
            Vector3 savedPosition = cameraTransform.position;
            Quaternion savedRotation = cameraTransform.rotation;

            // Set the camera position and rotation to default values during
            // rendering so they do not affect rendering
            cameraTransform.position = Vector3.zero;
            cameraTransform.rotation = Quaternion.identity;

            this.nativeWindow.Render();

            // Restore camera position and rotation
            cameraTransform.position = savedPosition;
            cameraTransform.rotation = savedRotation;
        }
    }

    private Rect m_priorRect = new Rect(float.NaN, float.NaN, float.NaN, float.NaN);
    private Rect m_priorCameraPixelRect = new Rect(float.NaN, float.NaN, float.NaN, float.NaN);

    private void UpdateNativeWindowProperties(bool force = false)
    {
        this.nativeWindow.transparent = this.transparent;

        if (force || (this.GetComponent<Camera>().pixelRect != this.m_priorCameraPixelRect) ||
            (this.rect != this.m_priorRect))
        {
            this.nativeWindow.bounds = this.bounds;
            this.m_priorRect = this.rect;
            this.m_priorCameraPixelRect = this.GetComponent<Camera>().pixelRect;
        }
    }
}
